<?php

class Data
{
	static public $db = null;
	
	static public function open()
	{
		// Data directory must always be writable
		is_writable(Config::get('path').'data') or
			die('<strong>CMS Error:</strong> Directory <em>'.Config::get('path').'data</em> has to be writable.');
		
		// Test database
		$db_filename = Config::get('path').'data/db.sqlite';
		if (file_exists($db_filename)) {
			is_writable($db_filename) or
				die('<strong>CMS Error:</strong> Database file <em>'.Config::get('path').'data/db.sqlite</em> has to be writable.');
		}
		
		// Open database
		self::$db = new PDO('sqlite:'.$db_filename) or
			die('<strong>CMS Error:</strong> Unexpected error while opening database file: '.$db_filename);
		
		self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
		
		Event::log('framework.data', 'Database opened');
	}
	
	static public function query($sql)
	{
		Event::log('framework.data', 'SQL Query: '.$sql, Event::LOG_DEBUG);
		return self::$db->query($sql);
	}
	
	/*
		Retrieves all result rows for a database query.
	*/
	static public function all($sql, $mode = PDO::FETCH_ASSOC)
	{
		$result = self::query($sql);
		
		if ($result) {
			return $result->fetchAll($mode);
		} else {
			return array();
		}
	}
	
	static public function first($sql, $mode = PDO::FETCH_ASSOC)
	{
		$result = self::query($sql);
		
		if ($result) {
			return $result->fetch($mode);
		} else {
			return array();
		}
	}
	
	static public function single($sql)
	{
		$row = self::first($sql, PDO::FETCH_NUM);
		
		return (isset($row[0])) ? $row[0] : null;
	}
	
	static public function insert($table, $data)
	{
		if (!is_array($data)) return;
		
		$table = self::sanitizeTableName($table);
		
		array_walk(&$data, array('Data', 'prepareValueCallback'));
		$keys = implode(', ', array_keys($data));
		$values = implode(', ', $data);
		
		self::query('INSERT INTO '.$table.' ('.$keys.') VALUES ('.$values.')');
		
		return self::$db->lastInsertId();
	}
	
	/**
	 * Update a database row.
	 *
	 * If $idData is null, this function will use the value for the field 'id'
	 * in the $updateData array.
	 * 
	 * If $idData is an empty array, the function will update ALL rows.
	 * 
	 * @param array $updateData Fields with respective new values
	 * @param array $idData List of columns by which to identify row
	 * @param bool $insertIfNotExists Whether to insert if row is not found
	 */
	static public function update($table, $updateData, $idData = null, $insertIfNotExists = false)
	{
		$table = self::sanitizeTableName($table);
		
		if ($idData === null) {
			isset($updateData['id']) or
				Event::log('framework.data', 'Can\'t update row, no ID provided.', Event::LOG_ERROR);
			
			$idData = array('id' => $updateData['id']);
			unset($updateData['id']);
		}
		
		$assignments = '';
		foreach ($updateData as $field => $value) {
			if (!empty($assignments)) $assignments .= ', ';
			
			$assignments .= self::sanitizeFieldName($field).'='.self::prepareValue($value);
		}
		
		if (is_array($idData)) {
			$conditions = '';
			foreach ($idData as $field => $value) {
				if (!empty($conditions)) $conditions .= ' AND ';
				
				$conditions .= self::sanitizeFieldName($field).' = '.self::prepareValue($value);
			}
		} else {
			// Could be an SQL conditional string, a number or a boolean
			$conditions = $idData;
		}
		
		$result = self::query("UPDATE $table SET $assignments WHERE $conditions");
		
		if ($insertIfNotExists && $result->rowCount() === 0) {
			// If we need to do a fresh insert, combine the two arrays
			foreach ($idData as $key => $value) {
				// The values in $updataData take precedence
				if (!isset($updateData[$key])) {
					$updateData[$key] = $value;
				}
			}
			
			// Now do the insert
			self::insert($table, $updateData);
		}
	}
	
	static public function prepareValue($val)
	{
		if (is_bool($val)) {
			return ($val) ? '1' : '0';
		} else if (is_string($val)) {
			return "'$val'";
		} else if (is_numeric($val)) {
			return $val;
		}
	}
	
	static public function prepareValueCallback(&$val)
	{
		$val = self::prepareValue($val);
	}
	
	static public function table_exists($table)
	{
		$result = self::query("SELECT COUNT(*) FROM sqlite_master WHERE name = '$table'");
		list($count) = $result->fetch();
		
		return $count > 0;
	}
	
	static protected function sanitizeTableName($table)
	{
		// TODO: stub
		return $table;
	}
	
	static protected function sanitizeFieldName($field)
	{
		// TODO: stub
		return $field;
	}
}

?>